package cn.liangxxhu.gateway.handler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * JDK动态代理的调用处理器实现类
 * 负责在代理对象调用目标方法时，插入增强逻辑（如参数校验、耗时统计、异常捕获、日志埋点等）
 * 核心作用：不修改目标对象源码的前提下，扩展目标方法的功能
 */
@Slf4j
@RequiredArgsConstructor
public class DebugInvocationHandler implements InvocationHandler {

    /**
     * 被代理的目标对象（真实业务逻辑的实现类）
     * final修饰确保初始化后不可修改，避免线程安全问题
     */
    private final Object target;

    /**
     * 代理对象的核心方法：目标方法调用时会自动触发此方法
     * 所有对代理对象方法的调用，最终都会转发到invoke方法中处理
     *
     * @param proxy  生成的代理对象实例（一般不直接使用，避免循环调用）
     * @param method 被调用的目标方法对象（包含方法名、参数类型、返回值等信息）
     * @param args   调用目标方法时传入的参数数组（无参数时为null）
     * @return 目标方法的执行结果（需与目标方法返回值类型一致）
     * @throws Throwable 目标方法执行过程中抛出的所有异常（需在业务层处理或向上抛出）
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 前置增强：方法执行前的准备操作（参数校验、权限检查、日志埋点等）
        long startTime = System.nanoTime(); // 记录方法开始执行时间（用于耗时统计）
        String methodFullName = getMethodFullName(method); // 获取方法全限定名（类名.方法名）

        // 1.1 打印方法调用的基础信息（替代原System.out，使用日志框架并包含更多上下文）
        log.info("【代理前置】开始调用方法：{}，调用参数：{}", methodFullName, formatArgs(args));

        // 1.2 目标方法参数校验（根据业务场景定制，此处以“参数非空”为例）
        validateMethodArgs(method, args);

        try {
            // 2. 调用目标对象的真实方法（核心业务逻辑执行）
            Object result = method.invoke(target, args);

            // 3. 后置增强：方法执行成功后的操作（结果日志、数据加工、缓存更新等）
            long costTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime); // 计算方法耗时（纳秒转毫秒）
            log.info("【代理后置】方法调用成功：{}，返回结果：{}，总耗时：{}ms", methodFullName, result, costTime);

            // 3.1 可选：对返回结果进行二次加工（示例：若返回String，统一添加前缀）
            result = processReturnResult(method, result);

            return result;

        } catch (Exception e) {
            // 4. 异常增强：方法执行异常时的处理（异常捕获、日志告警、重试机制等）
            long costTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
            log.error("【代理异常】方法调用失败：{}，异常信息：{}，总耗时：{}ms",
                    methodFullName, e.getMessage(), costTime, e); // 打印完整堆栈信息，便于排查

            // 4.1 可选：异常类型转换或重试（示例：将反射异常转为业务异常）
            throw convertToBusinessException(e);
        }
    }

    /**
     * 工具方法：获取方法的全限定名（包名.类名.方法名）
     * @param method 目标方法对象
     * @return 方法全限定名（如：cn.liangxxhu.gateway.service.SmsService.sendSms）
     */
    private String getMethodFullName(Method method) {
        Class<?> declaringClass = method.getDeclaringClass();
        return declaringClass.getName() + "." + method.getName();
    }

    /**
     * 工具方法：格式化方法参数（避免数组直接打印显示为内存地址）
     * @param args 方法参数数组
     * @return 格式化后的参数字符串（如：[138****1234, 您的验证码是1234]）
     */
    private String formatArgs(Object[] args) {
        if (args == null || args.length == 0) {
            return "无参数";
        }
        // 对敏感参数（如手机号）进行脱敏处理（示例：仅脱敏String类型的手机号）
        Object[] formattedArgs = Arrays.stream(args)
                .map(arg -> arg instanceof String ? maskSensitiveParam((String) arg) : arg)
                .toArray();
        return Arrays.toString(formattedArgs);
    }

    /**
     * 工具方法：敏感参数脱敏（示例：手机号中间4位替换为****，其他敏感信息可扩展）
     * @param param 原始参数
     * @return 脱敏后的参数
     */
    private String maskSensitiveParam(String param) {
        // 手机号脱敏（11位数字）
        if (param.matches("^1[3-9]\\d{9}$")) {
            return param.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        }
        // 其他敏感参数脱敏（如身份证号、银行卡号，可按需扩展）
        return param;
    }

    /**
     * 工具方法：目标方法参数校验（根据方法注解或业务规则校验）
     * @param method 目标方法
     * @param args 方法参数数组
     * @throws IllegalArgumentException 参数校验失败时抛出
     */
    private void validateMethodArgs(Method method, Object[] args) {
        // 示例1：若方法有@NotNull注解（需导入javax.validation.constraints.NotNull），校验参数非空
        // 此处简化为“指定方法必须有参数”（可根据实际业务扩展）
        String methodName = method.getName();
        if ("sendSms".equals(methodName) && (args == null || args.length < 2)) {
            throw new IllegalArgumentException("方法[" + methodName + "]必须传入2个参数（手机号、短信内容）");
        }

        // 示例2：校验手机号参数格式（若方法第一个参数是手机号）
        if (args != null && args.length > 0 && args[0] instanceof String) {
            String mobile = (String) args[0];
            if (mobile.matches("^1[3-9]\\d{9}$")) {
                return;
            }
            throw new IllegalArgumentException("参数[手机号]格式非法，正确格式为11位数字（如：13812345678）");
        }
    }

    /**
     * 工具方法：处理目标方法的返回结果（按需加工）
     * @param method 目标方法
     * @param result 原始返回结果
     * @return 加工后的返回结果
     */
    private Object processReturnResult(Method method, Object result) {
        // 示例：若方法返回值为String，统一添加“[代理增强]”前缀
        if (result instanceof String) {
            return "[代理增强]" + result;
        }
        // 其他类型结果可按需加工（如：集合类型去重、数值类型四舍五入等）
        return result;
    }

    /**
     * 工具方法：将反射调用异常转为业务异常（避免上层感知反射细节）
     * @param e 反射调用时抛出的异常
     * @return 业务异常
     */
    private Exception convertToBusinessException(Exception e) {
        // 反射调用的异常可能被包装在InvocationTargetException中，需先获取真实异常
        Throwable targetException = e instanceof java.lang.reflect.InvocationTargetException
                ? ((java.lang.reflect.InvocationTargetException) e).getTargetException()
                : e;

        // 示例：根据真实异常类型返回对应业务异常（需自定义BusinessException）
        if (targetException instanceof IllegalArgumentException) {
            return new RuntimeException("业务参数异常：" + targetException.getMessage(), targetException);
        } else {
            return new RuntimeException("业务执行异常：" + targetException.getMessage(), targetException);
        }
    }
}